package _case

import (
	"errors"
	"fmt"
	"reflect"
)

type Person struct {
	Name  string
	Age   int
	Hobby []string
}

func (p *Person) SayHello() {
	fmt.Printf("大家好,我的名字是：%s，年龄：\n", p.Name, p.Age)
}

type Users struct {
	Name  string
	Age   int
	Hobby []string
}

func ReflectCase() {
	person := Person{"Tom", 18, []string{"羽毛球", "篮球"}}

	p := Users{}
	err := cpStruct(&p, person)
	fmt.Println(err, p)

	listPerson := []Person{
		{"Lily", 18, []string{"羽毛球", "篮球"}},
		{"Nick", 16, []string{"游泳", "跑步"}},
	}
	slice := sliceColumn(listPerson, "Hobby")
	fmt.Println(slice)
}

func sliceColumn(slice interface{}, column string) interface{} {
	sType := reflect.TypeOf(slice)
	sValue := reflect.ValueOf(slice)
	if sType.Kind() == reflect.Ptr {
		sType = sType.Elem()
		sValue = sValue.Elem()
	}

	if sType.Kind() == reflect.Struct {
		val := sValue.FieldByName(column)
		return val.Interface()
	}

	if sValue.Kind() != reflect.Slice {
		return nil
	}

	t := sType.Elem()
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	f, ok := t.FieldByName(column)
	if !ok {
		return nil
	}

	sliceType := reflect.SliceOf(f.Type)
	s := reflect.MakeSlice(sliceType, 0, 0)
	for i := 0; i < sValue.Len(); i++ {
		o := sValue.Index(i)
		if o.Kind() == reflect.Struct {
			val := o.FieldByName(column)
			s = reflect.Append(s, val)
		}
		if o.Kind() == reflect.Ptr {
			v := o.Elem()
			val := v.FieldByName(column)
			s = reflect.Append(s, val)
		}
	}

	return s.Interface()
}

func cpStruct(dst, src interface{}) error {
	//使用反射获取类型和值
	srcType := reflect.TypeOf(src)
	srcValue := reflect.ValueOf(src)
	if srcType.Kind() == reflect.Ptr {
		srcType = srcType.Elem()
		srcValue = srcValue.Elem()
	}

	dstType := reflect.TypeOf(dst)
	dstValue := reflect.ValueOf(dst)
	if dstType.Kind() != reflect.Ptr {
		return errors.New("目标对象不是一个指针类型")
	}

	dstType = dstType.Elem()
	dstValue = dstValue.Elem()
	if srcValue.Kind() != reflect.Struct {
		return errors.New("源对象不是一个struct类型")
	}

	if dstValue.Kind() != reflect.Struct {
		return errors.New("目标对象不是一个struct类型")
	}

	dstObj := reflect.New(dstType)
	for i := 0; i < dstType.NumField(); i++ {
		field := dstType.Field(i)
		if sField, ok := srcType.FieldByName(field.Name); ok {
			if sField.Type != field.Type {
				continue
			}
			value := srcValue.FieldByName(sField.Name)
			dstObj.Elem().FieldByName(field.Name).Set(value)
		}
	}

	dstValue.Set(dstObj.Elem())

	return nil
}
